package io.legado.app

import cn.hutool.core.lang.JarClassLoader
import com.script.ScriptBindings
import com.script.rhino.RhinoScriptEngine
import dalvik.system.DexClassLoader
import org.intellij.lang.annotations.Language
import org.junit.Assert
import org.junit.Test
import org.mozilla.javascript.DefiningClassLoader
import java.net.URLClassLoader

class AndroidJsTest {

    @Test
    fun testPackages() {
        """
        测试函数testPackages，用于验证通过JavaScript代码执行签名算法的正确性。
        该函数主要执行以下操作：
        1. 定义JavaScript代码字符串，包含签名算法的实现。
        2. 使用RhinoScriptEngine执行JavaScript代码。
        3. 定义另一个JavaScript代码字符串，用于创建ReturnData对象并获取错误消息。
        4. 执行第二个JavaScript代码片段。
        5. 使用JUnit的Assert.assertEquals验证执行结果是否符合预期。
        """
        @Language("js")
        val js = """
            // 定义访问密钥ID、密钥秘密、时间戳等变量。
            var accessKeyId = '1111';
            var accessKeySecret = '2222';
            var timestamp = '3333';
            // 导入Java类，用于执行签名算法。
            var aly = new JavaImporter(Packages.javax.crypto.Mac, Packages.javax.crypto.spec.SecretKeySpec, Packages.javax.xml.bind.DatatypeConverter, Packages.java.net.URLEncoder, Packages.java.lang.String, Packages.android.util.Base64);
            with (aly) {
                // 定义百分比编码函数，用于对URL参数进行编码。
                function percentEncode(value) {
                    return URLEncoder.encode(value, "UTF-8").replace("+", "%20")
                        .replace("*", "%2A").replace("%7E", "~")
                }
            
                // 定义签名函数，使用HmacSHA1算法对给定字符串进行签名。
                function sign(stringToSign, accessKeySecret) {
                    var mac = Mac.getInstance('HmacSHA1');
                    mac.init(new SecretKeySpec(String(accessKeySecret + '&').getBytes("UTF-8"), "HmacSHA1"));
                    var signData = mac.doFinal(String(stringToSign).getBytes("UTF-8"));
                    var signBase64 = Base64.encodeToString(signData, Base64.NO_WRAP);
                    var signUrlEncode = percentEncode(signBase64);
                    return signUrlEncode;
                }
            }
            // 构造查询字符串，包含所有请求参数。
            var query = 'AccessKeyId=' + accessKeyId + '&Action=CreateToken&Format=JSON&RegionId=cn-shanghai&SignatureMethod=HMAC-SHA1&SignatureNonce=' + "xxccrr" + '&SignatureVersion=1.0&Timestamp=' + percentEncode(timestamp) + '&Version=2019-02-28';
            // 执行签名函数，并构造带有签名的查询字符串。
            var signStr = sign('GET&' + percentEncode('/') + '&' + percentEncode(query), accessKeySecret);
            var queryStringWithSign = "Signature=" + signStr + "&" + query;
            queryStringWithSign
        """.trimIndent()
        RhinoScriptEngine.eval(js)
        @Language("js")
        val js1 = """
            // 创建ReturnData对象并获取错误消息。
            var returnData = new Packages.io.legado.app.api.ReturnData()
            returnData.getErrorMsg()
        """.trimIndent()
        val result1 = RhinoScriptEngine.eval(js1)
        // 验证错误消息是否符合预期。
        Assert.assertEquals(result1, "未知错误,请联系开发者!")
    }

    /**
     * 测试不同类加载器的系统类加载器方法
     *
     * 该测试方法的目的是验证各种类加载器的系统类加载器方法的行为，包括URLClassLoader、DefiningClassLoader、JarClassLoader和DexClassLoader
     * 此外，它还演示了如何在Kotlin代码中使用JavaScript通过RhinoScriptEngine执行
     */
    @Test
    fun testPackages1() {
        // 获取系统类加载器，用于加载标准Java类库
        URLClassLoader.getSystemClassLoader()
        // 获取系统类加载器，特定于DefiningClassLoader，可能用于加载自定义或特定于应用程序的类
        DefiningClassLoader.getSystemClassLoader()
        // 获取系统类加载器，特定于JarClassLoader，可能用于处理jar文件中的类加载
        JarClassLoader.getSystemClassLoader()
        // 获取系统类加载器，特定于DexClassLoader，通常用于在Android环境中加载Dex文件格式的类
        DexClassLoader.getSystemClassLoader()

        // 定义一段JavaScript代码，演示如何在Kotlin中嵌入和执行JavaScript
        @Language("js")
        val js = """
            var ji = new JavaImporter(Packages.org.mozilla.javascript.DefiningClassLoader)
            with(ji) {
              let x = DefiningClassLoader.getSystemClassLoader()
            }
        """.trimIndent()

        // 使用RhinoScriptEngine评估和执行前面定义的JavaScript代码
        RhinoScriptEngine.eval(js)
    }

    @Test
    fun testMap() {
        // 创建一个HashMap实例，键为"id"，值为"3242532321"
        val map = hashMapOf("id" to "3242532321")

        // 初始化一个ScriptBindings对象，用于存储脚本执行的上下文变量
        val bindings = ScriptBindings()

        // 将map对象添加到bindings中，键为"result"
        bindings["result"] = map

        // 定义一段JavaScript代码，用于从bindings中获取"id"键对应的值
        @Language("js")
        val jsMap = "$=result;id=$.id;id"

        // 使用Rhino脚本引擎执行JavaScript代码，并获取执行结果
        val result = RhinoScriptEngine.eval(jsMap, bindings)

        // 断言执行结果与预期值"3242532321"相等
        Assert.assertEquals("3242532321", result)

        // 定义另一段JavaScript代码，通过get方法从bindings中的map对象获取"id"键的值
        @Language("js")
        val jsMap1 = """result.get("id")"""

        // 使用Rhino脚本引擎执行这段JavaScript代码，并获取执行结果
        val result1 = RhinoScriptEngine.eval(jsMap1, bindings)

        // 断言执行结果与预期值"3242532321"相等
        Assert.assertEquals("3242532321", result1)
    }

}